﻿/****************************************************************************************
*	Mouzetrap - client/server framework	
*
*	$HeadURL: http://mouzetrap.googlecode.com/svn/trunk/Mouzetrap/Threading/SpinWaitLock.cs $
*	$Id: SpinWaitLock.cs 11 2009-06-11 09:10:25Z tobz@fallenrealms.net $
* 
*	Original copyright (c) 2005 Jeffrey Richter ("Concurrent Affairs", October 2005, MSDN Magazine)
* 
*	Copyright (c) 2008 Toby Lawrence
* 
*	Permission is hereby granted, free of charge, to any person obtaining a copy
*	of this software and associated documentation files (the "Software"), to deal
*	in the Software without restriction, including without limitation the rights
*	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
*	copies of the Software, and to permit persons to whom the Software is
*	furnished to do so, subject to the following conditions:
* 
*	The above copyright notice and this permission notice shall be included in
*	all copies or substantial portions of the Software.
* 
*	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
*	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
*	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
*	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
*	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
*	THE SOFTWARE.
* 
****************************************************************************************/

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace Mouzetrap.Threading
{
	/// <summary>
	/// Implements a lock using a spin wait (busy wait) to achieve concurrency.
	/// </summary>
	/// <remarks>
	/// This structure is based on Jeffrey Richter's article "Concurrent Affairs" in the October 2005 
	/// issue of MSDN Magazine.
	/// 
	/// Using this as a field will work very efficient.  Do not box this, or you will lose thread-safety!
	/// 
	/// Also, this lock is not fair (ordered); anybody has a chance to grab it when a thread gives it up.
	/// </remarks>
	public struct SpinWaitLock
	{
		private const int LOCK_FREE = 0;
		private const int LOCK_OWNED = 1;
		private static readonly bool IsSingleCpuMachine = (Environment.ProcessorCount == 1);

		private int _lockState;

		/// <summary>
		/// Attempts to enter the lock.
		/// </summary>
		public void Enter()
		{
			Thread.BeginCriticalRegion();

			while(true)
			{
				// if lock is free, set it to owned and return
				if(Interlocked.Exchange(ref _lockState, LOCK_OWNED) == LOCK_FREE)
					return;

				// keep spinning until the lock appears to be free, at which point 
				// we'll reloop and try to acquire the lock again.
				//
				// NOTE: doing a volatile read here is the fastest way to keep 
				// checking the latest state of the lock, rather than attepting
				// to call Exchange over and over again.
				while(Thread.VolatileRead(ref _lockState) == LOCK_OWNED)
				{
					StallThread();
				}
			}
		}

		/// <summary>
		/// Releases the lock.
		/// </summary>
		public void Exit()
		{
			// mark the lock as free
			Interlocked.Exchange(ref _lockState, LOCK_FREE);

			Thread.EndCriticalRegion();
		}

#if LINUX
        private static void StallThread()
        {
            //Linux doesn't support SwitchToThread()
            Thread.SpinWait(1);
        }
#else
		private static void StallThread()
		{
			if(IsSingleCpuMachine)
			{
				// can't spin on a single CPU system, we're forced to yield our thread.
				SwitchToThread();
			}
			else
			{
				// spin here so other threads can run.
				Thread.SpinWait(1);
			}
		}

		[DllImport("kernel32", ExactSpelling = true)]
		private static extern void SwitchToThread();
#endif
	}
}